package org.dxy.domain.model.aggregate.impl;

import lombok.Builder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.dxy.domain.command.equipment.*;
import org.dxy.domain.event.DomainEvent;
import org.dxy.domain.event.equipment.*;
import org.dxy.domain.exception.EquipmentAlreadyDeactivatedException;
import org.dxy.domain.exception.EquipmentCannotFailProcessException;
import org.dxy.domain.model.aggregate.Equipment;
import org.dxy.domain.model.valobj.equipment.EquipmentId;
import org.dxy.types.enums.EquipmentStatusEnum;
import java.time.LocalDateTime;
import java.util.*;
import static org.dxy.types.enums.EquipmentStatusEnum.ACTIVATED;
import static org.dxy.types.enums.EquipmentStatusEnum.REGISTERED;

/**
 * @ description: 设备聚合根实现类
 * @ author: dxy
 * @ createTime: 2025/6/22 17:26
 */

@Slf4j
@Getter
public class EquipmentAggRoot implements Equipment {

    // 设备领域属性
    private EquipmentId equipmentId;          // 设备ID
    private EquipmentStatusEnum status;       // 设备状态
    private Long currentWorkOrderId;          // 当前执行的工单ID
    private Long currentProcessStepId;        // 当前执行的工艺步骤ID


    // === 事件集合 ===
    private final List<DomainEvent> domainEvents = new ArrayList<>();

    // === 事件相关方法 ===
    @Override
    public List<DomainEvent> getDomainEvents() {
        return Collections.unmodifiableList(domainEvents); // 返回一个 不可修改的视图
    }

    @Override
    public void addDomainEvent(DomainEvent event) {
        domainEvents.add(event);
    }

    @Override
    public void clearDomainEvents() {
        domainEvents.clear();
    }

    @Override
    public boolean hasDomainEvents() {
        return !domainEvents.isEmpty();
    }

    @Override
    public boolean hasNoDomainEvents() {
        return domainEvents.isEmpty();
    }

    // === 构造函数 ===
    public EquipmentAggRoot(EquipmentId equipmentId, EquipmentStatusEnum status) {
        this.equipmentId = equipmentId;
        this.status = status;
    }

    // === 静态工厂方法 ===
    public static Equipment register(Long equipmentId, RegisterEquipmentCommand command) {
        EquipmentAggRoot equipment = new EquipmentAggRoot(
                EquipmentId.of(equipmentId),
                EquipmentStatusEnum.REGISTERED
        );

        EquipmentRegisteredEvent event = EquipmentRegisteredEvent.builder()
                .equipmentId(equipmentId)
                .equipmentType(command.getEquipmentType())
                .serialNumber(command.getSerialNumber())
                .build();
        // 创建并记录事件
        equipment.addDomainEvent(event);

        return equipment;
    }

    // === 业务方法实现 ===
    @Override
    public void assignProcess(Long workOrderId, Long processStepId) {
        if (workOrderId == null || processStepId == null) {
            throw new IllegalArgumentException("工单ID和工序步骤ID不能为空");
        }

        // 只有 IDLE 状态的设备才可以被分配任务
        if (this.status != EquipmentStatusEnum.IDLE) {
            throw new IllegalStateException("设备当前状态为 " + status.getDescription() + "，不能分配新任务");
        }

        this.currentWorkOrderId = workOrderId;
        this.currentProcessStepId = processStepId;
        this.status = EquipmentStatusEnum.WORKING;
        log.info("设备 {} 任务分配成功，状态更新为 WORKING，当前工单ID：{}，工序步骤ID：{}", equipmentId.getValue(), workOrderId, processStepId);
    }

    @Override
    public void reportExecutionResult(ReportProcessResultCommand command) {
        Long workOrderId = command.getWorkOrderId();
        Long processStepId = command.getProcessStepId();
        Boolean success = command.getSuccess();
        if (workOrderId == null || processStepId == null) {
            throw new IllegalArgumentException("工单ID和工序步骤ID不能为空");
        }

        if (!workOrderId.equals(this.currentWorkOrderId) || !processStepId.equals(this.currentProcessStepId)) {
            throw new IllegalStateException("上报的工单步骤与设备当前任务不匹配");
        }

        if (success) {
            this.status = EquipmentStatusEnum.IDLE;
            log.info("设备 {} 完成工单 {} 步骤 {}，状态设为 IDLE", equipmentId.getValue(), workOrderId, processStepId);
        } else {
            this.status = EquipmentStatusEnum.FAULT;
            log.error("设备 {} 工单 {} 步骤 {} 执行失败，状态设为 FAULT", equipmentId.getValue(), workOrderId, processStepId);
        }


        if (this.status == EquipmentStatusEnum.IDLE) {
            this.currentWorkOrderId = null;
            this.currentProcessStepId = null;
        }
    }

    @Override
    public void activate(ActivateEquipmentCommand command) {
        if (!REGISTERED.equals(this.status)) {
            throw new IllegalStateException("只有已注册设备才能激活");
        }

        String previousStatus = this.status.getKey();
        this.status = ACTIVATED;

        EquipmentActivatedEvent event = EquipmentActivatedEvent.builder()
                .equipmentId(this.equipmentId.getValue())
                .previousStatus(previousStatus)
                .currentStatus(this.status.getKey())
                .build();

        this.addDomainEvent(event);  // 添加事件到聚合事件列表
    }



    @Override
    public void configureCapabilities(ConfigureEquipmentCapabilityCommand command) {

        // 生成事件
        EquipmentCapabilityConfiguredEvent event = EquipmentCapabilityConfiguredEvent.builder()
                .equipmentId(this.getEquipmentId().getValue())
                .build();

        // 添加事件到聚合事件列表
        this.addDomainEvent(event);
    }


    @Override
    public void deactivate(DeactivateEquipmentCommand reason) {
        if (this.status == EquipmentStatusEnum.DEACTIVATED) {
            throw new EquipmentAlreadyDeactivatedException(this.getEquipmentId().getValue());
        }

        this.status = EquipmentStatusEnum.DEACTIVATED;

        EquipmentDeactivatedEvent event = EquipmentDeactivatedEvent.builder()
                .equipmentId(this.getEquipmentId().getValue())
                .build();

        this.addDomainEvent(event);
    }


    @Override
    public void assignTask(AssignProcessToEquipmentCommand command) {
        // Step 1: 状态校验，确保设备可分配任务
        if (!this.status.isDispatchable()) {
            throw new IllegalStateException("设备当前状态不可分配任务: " + this.status);
        }

        // Step 2: 设置设备当前任务信息
        this.currentWorkOrderId = command.getWorkOrderId();
        this.currentProcessStepId = command.getProcessStepId();

        // Step 3: 更新设备状态（假设进入"执行中"或"占用中"状态）
        this.status = EquipmentStatusEnum.WORKING;

        // Step 4: 构建领域事件并添加到事件列表
        TaskAssignedToEquipmentEvent event = TaskAssignedToEquipmentEvent.builder()
                .workOrderId(command.getWorkOrderId())
                .processStepId(command.getProcessStepId())
                .equipmentId(this.getEquipmentId().getValue())
                .assignedTime(LocalDateTime.now())
                .build();

        this.addDomainEvent(event);
    }

    @Override
    public void completeProcess(CompleteProcessCommand command) {
        // 1. 校验设备状态，确保设备处于执行任务状态
        if (this.status != EquipmentStatusEnum.WORKING) {
            throw new IllegalStateException("设备当前状态不允许完成任务: " + this.status);
        }

        // 2. 更新设备状态为空闲，表示任务完成且设备空闲
        this.status = EquipmentStatusEnum.IDLE;

        // 3. 清理当前任务信息
        this.currentWorkOrderId = null;
        this.currentProcessStepId = null;

        // 4. 构造任务完成事件并加入事件列表
        ProcessCompletedEvent event = ProcessCompletedEvent.builder()
                .workOrderId(command.getWorkOrderId())
                .processStepId(command.getProcessStepId())
                .equipmentId(this.getEquipmentId().getValue())
                .build();

        this.addDomainEvent(event);
    }

    @Override
    public ProcessStartedEvent startProcess(StartProcessCommand command) {
        // 1. 校验设备状态，设备必须处于已激活或空闲状态才能开始任务
        if (!(this.status == EquipmentStatusEnum.ACTIVATED || this.status == EquipmentStatusEnum.IDLE)) {
            throw new IllegalStateException("设备当前状态不允许开始任务: " + this.status);
        }

        // 2. 更新设备状态为工作中
        this.status = EquipmentStatusEnum.WORKING;

        // 3. 记录当前任务
        this.currentWorkOrderId = command.getWorkOrderId();
        this.currentProcessStepId = command.getProcessStepId();

        // 4. 构造并返回任务开始事件
        return ProcessStartedEvent.builder()
                .workOrderId(command.getWorkOrderId())
                .processStepId(command.getProcessStepId())
                .equipmentId(this.getEquipmentId().getValue())
                .startTime(LocalDateTime.now())
                .build();
    }

    @Override
    public ResultReportedEvent reportResult(ReportProcessResultCommand command) {
        // 1. 校验设备状态，确保当前设备处于允许上报结果的状态
        if (!(this.status == EquipmentStatusEnum.WORKING || this.status == EquipmentStatusEnum.ACTIVATED)) {
            throw new IllegalStateException("设备当前状态不允许上报结果: " + this.status);
        }

        // 2. 更新设备状态为空闲，表示任务已完成且设备空闲
        this.status = EquipmentStatusEnum.IDLE;

        // 3. 清理当前任务相关信息
        this.currentWorkOrderId = null;
        this.currentProcessStepId = null;

        // 4. 构造并返回处理结果上报事件
        return ResultReportedEvent.builder()
                .workOrderId(command.getWorkOrderId())
                .processStepId(command.getProcessStepId())
                .equipmentId(this.getEquipmentId().getValue())
                .build();
    }

    @Override
    public ProcessFailedEvent failProcess(FailProcessCommand command) {
        // 1. 校验设备状态，只有 WORKING 状态的设备才允许执行失败处理
        if (this.status != EquipmentStatusEnum.WORKING) {
            throw new EquipmentCannotFailProcessException(command.getEquipmentId(), this.status);
        }

        // 2. 更新设备状态为空闲
        this.status = EquipmentStatusEnum.IDLE;

        // 3. 清理当前任务信息
        this.currentWorkOrderId = null;
        this.currentProcessStepId = null;

        // 4. 构造并返回失败事件
        return ProcessFailedEvent.builder()
                .workOrderId(command.getWorkOrderId())
                .processStepId(command.getProcessStepId())
                .equipmentId(this.getEquipmentId().getValue())
                .build();
    }

    @Override
    public EquipmentFaultDetectedEvent detectFault(DetectEquipmentFaultCommand command) {
        this.status = EquipmentStatusEnum.FAULT;

        return EquipmentFaultDetectedEvent.builder()
                .equipmentId(this.getEquipmentId().getValue())
                .build();
    }


    @Override
    public EquipmentRecoveredEvent recover(RecoverEquipmentCommand command) {
        // 如果当前状态不是故障，不能执行恢复操作
        if (this.status != EquipmentStatusEnum.FAULT) {
            throw new IllegalStateException("设备当前不处于故障状态，无法执行恢复操作，当前状态为: " + this.status);
        }

        // 设置为可调度状态
        this.status = EquipmentStatusEnum.IDLE;

        return EquipmentRecoveredEvent.builder()
                .equipmentId(this.getEquipmentId().getValue())
                .build();
    }

    @Override
    public EquipmentMaintenanceStartedEvent startMaintenance(StartEquipmentMaintenanceCommand command) {
        if (this.status != EquipmentStatusEnum.IDLE && this.status != EquipmentStatusEnum.ACTIVATED) {
            throw new IllegalStateException("只有空闲或可调度状态下的设备才能进入维护状态，当前状态为: " + this.status);
        }

        this.status = EquipmentStatusEnum.MAINTENANCE;

        return EquipmentMaintenanceStartedEvent.builder()
                .equipmentId(this.getEquipmentId().getValue())
                .build();
    }

    @Override
    public void completeMaintenance(CompleteEquipmentMaintenanceCommand command) {
        if (this.status != EquipmentStatusEnum.MAINTENANCE) {
            throw new IllegalStateException("设备当前不处于维护状态，无法完成维护，当前状态为: " + this.status);
        }

        this.status = EquipmentStatusEnum.IDLE;

        EquipmentMaintenanceCompletedEvent event = EquipmentMaintenanceCompletedEvent.builder()
                .equipmentId(this.getEquipmentId().getValue())
                .build();

        this.addDomainEvent(event);
    }

    @Override
    public EquipmentInspectionPassedEvent passInspection(PassEquipmentInspectionCommand command) {
        return EquipmentInspectionPassedEvent.builder()
                .equipmentId(this.getEquipmentId().getValue())
                .build();
    }

    @Override
    public EquipmentInspectionFailedEvent failInspection(FailEquipmentInspectionCommand command) {
        return EquipmentInspectionFailedEvent.builder()
                .equipmentId(this.getEquipmentId().getValue())
                .build();
    }

    @Override
    public EquipmentRetiredEvent retire(RetireEquipmentCommand command) {
        if (this.status == EquipmentStatusEnum.DEACTIVATED) {
            throw new IllegalStateException("设备已停用，不能退役");
        }

        this.status = EquipmentStatusEnum.RETIRED;

        return EquipmentRetiredEvent.builder()
                .equipmentId(this.equipmentId.getValue())
                .build();
    }

    @Override
    public EquipmentDeletedEvent delete(DeleteEquipmentCommand command) {
        if (this.status != EquipmentStatusEnum.RETIRED) {
            throw new IllegalStateException("设备未退役，不能执行删除");
        }

        return EquipmentDeletedEvent.builder()
                .equipmentId(this.equipmentId.getValue())
                .build();
    }

    @Override
    public void reassignTaskDueToFailure(ReassignTaskDueToFailureCommand command) {

    }

}
